/*
 * Copyright 2019,2021-2023 NXP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef UWBIOT_USE_FTR_FILE
#include "uwb_iot_ftr.h"
#else
#include "uwb_iot_ftr_default.h"
#endif
#include "phUwbTypes.h"

#include "phNxpUwb_Socket_const.h"
#include "phUwb_BuildConfig.h"
#include "phOsalUwb.h"
#include "UWB_Evt.h"
#include "phUwbErrorCodes.h"
#include <uwb_uwbs_tml_interface.h>
#include <phNxpLogApis_TmlUwb.h>
#include <uwb_bus_board.h>

void board_closesocket();
int board_opensocket(char *SocketName, size_t socketNameLen);
int board_socket_writeData(uint8_t *input, uint16_t inLen, uint8_t *output, uint16_t *outLen);

static uint8_t tx_buffer[4200];

#define PAYLOAD_LEN_MSB  0x02
#define PAYLOAD_LEN_LSB  0x03
#define UCI_HDR_LEN      0x04
#define HBCI_HEADER_SIZE 0x04

#define NORMAL_MODE_LEN_OFFSET          0x03
#define EXTND_LEN_INDICATOR_OFFSET      0x01
#define EXTND_LEN_INDICATOR_OFFSET_MASK 0x80
#define EXTENDED_LENGTH_OFFSET          0x02

#define HDLL_HDR_LEN          0x02
#define HDLL_EDL_LEN          0x02
#define HDLL_RESPONSE_LEN_MSB 0x00
#define HDLL_RESPONSE_LEN_LSB 0x01
#define HDLL_CRC_BYTES_LEN    0x02

#define PROTOCOL_OPERATION_GROUP_EDL           0x03
#define EDL_OP_GROUP_DOWNLOAD_FLASH_WRITE_LAST 0x04

#define UCI_CMD             0x01
#define HBCI_CMD            0x02
#define HBCI_LAST_CMD       0x03
#define RESET               0x04
#define HBCI_QUERY_CMD      0x05
#define MCU_RESET           0x06 // MCU nvReset will be called.
#define RCI_CMD             0x0A
#define SWITCH_PROTOCOL_CMD 0x0B
#define QUERY_BOARD_CMD     0x0C
#define HDLL_CMD            0x0E
#define HDLL_EDL_LAST_WRITE 0x0F
#define HDLL_GET_NTF        0x10
#define HELIOS2_RESET       0x11
#define HDLL_RESET_TO_UCI   0x12

UWBStatus_t uwb_uwbs_tml_init(uwb_uwbs_tml_ctx_t *pCtx)
{
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    uint8_t rx_buffer[4]        = {0};
    size_t socketNameLen        = sizeof(pCtx->busCtx.socketName) - 1;
    board_SerialGetSocket(pCtx->busCtx.socketName, &socketNameLen);

    bus_status = uwb_bus_init(&pCtx->busCtx);
    if (bus_status != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_init : uwb_bus_init failed");
        goto end;
    }

    tx_buffer[0] = RESET;
    tx_buffer[1] = 0x00;
    tx_buffer[2] = 0x00;

    bus_status = uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, 3);
    if (bus_status != kUWB_bus_Status_OK) {
        goto end;
    }

    phOsalUwb_Delay(150); /* wait for some time for writer */

    bus_status = uwb_bus_data_rx(&pCtx->busCtx, rx_buffer, UCI_HDR_LEN);
    if (bus_status != kUWB_bus_Status_OK) {
        goto end;
    }

    status = kUWBSTATUS_SUCCESS;
end:
    return status;
}

UWBStatus_t uwb_uwbs_tml_setmode(uwb_uwbs_tml_ctx_t *pCtx, uwb_uwbs_tml_mode_t mode)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }
    pCtx->mode = mode;
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_deinit(uwb_uwbs_tml_ctx_t *pCtx)
{
    tx_buffer[0] = UWB_SKT_DEINIT_FUNC;
    tx_buffer[1] = 0;
    tx_buffer[2] = 0;
    tx_buffer[3] = 0;
    tx_buffer[4] = 0;

    uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, 5);
    uwb_bus_deinit(&pCtx->busCtx);
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_data_tx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t bufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    pCtx->noOfBytesWritten      = -1;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pBuf == NULL || bufLen == 0) {
        LOG_E("write buffer is Null or bufLen is 0");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    tx_buffer[1] = (bufLen >> 8u * 1u) & 0xFFu;
    tx_buffer[2] = (bufLen >> 8u * 0u) & 0xFFu;
    phOsalUwb_MemCopy(&tx_buffer[3], pBuf, (uint32_t)bufLen);

    switch (pCtx->mode) {
    case kUWB_UWBS_TML_MODE_UCI: {
        tx_buffer[0] = UCI_CMD;
    } break;
#if UWBIOT_UWBD_SR1XXT
    case kUWB_UWBS_TML_MODE_HBCI: {
        if (pBuf[0] == 0x51 && pBuf[1] == 0x1 && bufLen == 4) {
            tx_buffer[0] = HBCI_LAST_CMD;
        }
        else {
            tx_buffer[0] = HBCI_CMD;
        }
    } break;
#endif
#if UWBIOT_UWBD_SR2XXT
    case kUWB_UWBS_TML_MODE_HDLL: {
        if (pBuf[2] == PROTOCOL_OPERATION_GROUP_EDL && pBuf[3] == EDL_OP_GROUP_DOWNLOAD_FLASH_WRITE_LAST) {
            tx_buffer[0] = HDLL_EDL_LAST_WRITE;
        }
        else {
            tx_buffer[0] = HDLL_CMD;
        }
    } break;
#endif
#if UWBIOT_UWBD_SR040
    case kUWB_UWBS_TML_MODE_SWUP: {
        tx_buffer[0] = RCI_CMD;
    } break;
#endif
    default:
        LOG_E("TML mode is not supported");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    bus_status = uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, 3 + bufLen);
    if (bus_status != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_data_tx : uwb_bus_data_tx failed");
        goto end;
    }
    status                 = kUWBSTATUS_SUCCESS;
    pCtx->noOfBytesWritten = (uint32_t)bufLen;
end:
    return status;
}

UWBStatus_t uwb_uwbs_tml_data_rx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t *pBufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    uint16_t payloadLen, frameHeaderLen;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pBuf == NULL || pBufLen == NULL) {
        LOG_E("read buffer or bufLen is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }
    *pBufLen = 0;

    if (pCtx->mode == kUWB_UWBS_TML_MODE_UCI || pCtx->mode == kUWB_UWBS_TML_MODE_HBCI ||
        pCtx->mode == kUWB_UWBS_TML_MODE_SWUP) {
        /* the header length for all 3 modes is the same (4 bytes)*/
        frameHeaderLen = UCI_HDR_LEN;
    }
    else if (pCtx->mode == kUWB_UWBS_TML_MODE_HDLL) {
        frameHeaderLen = HDLL_HDR_LEN;
    }
    else {
        LOG_E("UWB UWBS TML Mode is not supported");
        goto end;
    }

    bus_status = uwb_bus_data_rx(&pCtx->busCtx, pBuf, frameHeaderLen);
    if (bus_status == kUWB_bus_Status_OK) {
        if (pCtx->mode == kUWB_UWBS_TML_MODE_UCI) {
            uint32_t IsExtndLenIndication = 0;
            uint16_t totalBtyesToRead     = 0;

            IsExtndLenIndication = (pBuf[EXTND_LEN_INDICATOR_OFFSET] & EXTND_LEN_INDICATOR_OFFSET_MASK);
            totalBtyesToRead     = pBuf[NORMAL_MODE_LEN_OFFSET];
            if (IsExtndLenIndication) {
                totalBtyesToRead = (uint16_t)((totalBtyesToRead << 8) | pBuf[EXTENDED_LENGTH_OFFSET]);
            }
            payloadLen = totalBtyesToRead;
        }
        else if (pCtx->mode == kUWB_UWBS_TML_MODE_HBCI || pCtx->mode == kUWB_UWBS_TML_MODE_SWUP) {
            payloadLen = (uint16_t)((pBuf[PAYLOAD_LEN_MSB] << 8) | pBuf[PAYLOAD_LEN_LSB]);
        }
        else if (pCtx->mode == kUWB_UWBS_TML_MODE_HDLL) {
            payloadLen = (uint16_t)((((pBuf[HDLL_RESPONSE_LEN_MSB] & 0x1F) << 8) | pBuf[HDLL_RESPONSE_LEN_LSB]) +
                                    HDLL_CRC_BYTES_LEN);
        }
        else {
            LOG_E("UWB UWBS TML Mode is not supported");
            goto end;
        }

        if (payloadLen != 0) {
            bus_status = uwb_bus_data_rx(&pCtx->busCtx, &pBuf[frameHeaderLen], payloadLen);
            if (bus_status != kUWB_bus_Status_OK) {
                goto end;
            }
        }
        *pBufLen = (frameHeaderLen + payloadLen);
    }
    else {
        goto end;
    }
    status = kUWBSTATUS_SUCCESS;
end:
    return status;
}

#if !(UWBIOT_UWBD_SR040)
UWBStatus_t uwb_uwbs_tml_data_trx(
    uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pTxBuf, size_t txBufLen, uint8_t *pRxBuf, size_t *pRxBufLen)
{
    if (uwb_uwbs_tml_data_tx(pCtx, pTxBuf, txBufLen) != UWBSTATUS_SUCCESS) {
        LOG_E("uwb_uwbs_tml_data_trx : uwb_uwbs_tml_data_tx failed");
        return UWBSTATUS_FAILED;
    }

    phOsalUwb_Delay(20);

    if (uwb_uwbs_tml_data_rx(pCtx, pRxBuf, pRxBufLen) != UWBSTATUS_SUCCESS) {
        LOG_E("uwb_uwbs_tml_data_trx : uwb_uwbs_tml_data_rx failed");
        return UWBSTATUS_FAILED;
    }

    return UWBSTATUS_SUCCESS;
}
#endif // !(UWBIOT_UWBD_SR040)

UWBStatus_t uwb_uwbs_tml_query_board(uwb_uwbs_tml_ctx_t *pCtx)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    tx_buffer[0] = QUERY_BOARD_CMD;
    tx_buffer[1] = 0x00;
    tx_buffer[2] = 0x00;

    if (uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, 3) != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_query_board : uwb_bus_data_tx failed");
        return UWBSTATUS_FAILED;
    }
    pCtx->boardVersion = 0x00;
    if (uwb_bus_data_rx(&pCtx->busCtx, &pCtx->boardVersion, 1) != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_query_board : uwb_bus_data_rx failed");
        return UWBSTATUS_FAILED;
    }
    return UWBSTATUS_SUCCESS;
}

#if UWBIOT_UWBD_SR040

UWBStatus_t uwb_uwbs_tml_reset(uwb_uwbs_tml_ctx_t *pCtx)
{
    /* Not used in UART tml */
    return kUWBSTATUS_SUCCESS;
}

void uwb_uwbs_tml_flush_read_buffer(uwb_uwbs_tml_ctx_t *pCtx)
{
    /* Not needed here*/
    return;
}

#endif // UWBIOT_UWBD_SR040

#if UWBIOT_UWBD_SR2XXT
UWBStatus_t uwb_uwbs_tml_helios_reset(uwb_uwbs_tml_ctx_t *pCtx, bool isFWDownloadDone)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    tx_buffer[0] = HELIOS2_RESET;
    tx_buffer[1] = 0x00;
    tx_buffer[2] = sizeof(bool);
    tx_buffer[3] = isFWDownloadDone;

    if (uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, 4) != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_query_board : uwb_bus_data_tx failed");
        return UWBSTATUS_FAILED;
    }
    return UWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_helios_get_hdll_edl_ntf(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pRxBuf, size_t *pRxBufLen)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    tx_buffer[0] = HDLL_GET_NTF;
    tx_buffer[1] = 0x00;
    tx_buffer[2] = 0x00;

    if (uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, 3) != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_query_board : uwb_bus_data_tx failed");
        return UWBSTATUS_FAILED;
    }
    phOsalUwb_Delay(10);
    if (uwb_uwbs_tml_data_rx(pCtx, pRxBuf, pRxBufLen) != UWBSTATUS_SUCCESS) {
        LOG_E("uwb_uwbs_tml_data_trx : uwb_uwbs_tml_data_rx failed");
        return UWBSTATUS_FAILED;
    }
    return UWBSTATUS_SUCCESS;
}

#endif // UWBIOT_UWBD_SR2XXT

#if !(UWBIOT_UWBD_SR040)
UWBStatus_t uwb_uwbs_tml_helios_hardreset(uwb_uwbs_tml_ctx_t *pCtx)
{
    UWBStatus_t status;
    uint8_t rx_buffer[5];
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }

    tx_buffer[0] = RESET;
    tx_buffer[1] = 0x00;
    tx_buffer[2] = 0x00;

    if (uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, 3) != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_query_board : uwb_bus_data_tx failed");
        return UWBSTATUS_FAILED;
    }

    phOsalUwb_Delay(150);

    status = uwb_bus_data_rx(&pCtx->busCtx, rx_buffer, UCI_HDR_LEN);
    if (status != kUWB_bus_Status_OK) {
        return UWBSTATUS_FAILED;
    }

    return UWBSTATUS_SUCCESS;
}
#endif // !(UWBIOT_UWBD_SR040)
